package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 报警数据Service业务层处理
 * 
 * @author ZHD
 * @date 2025-08-26
 */
@Service
public class AlertDataServiceImpl extends ServiceImpl<AlertDataMapper, AlertData> implements IAlertDataService
{
    @Autowired
    private AlertDataMapper alertDataMapper;
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 查询报警数据
     * 
     * @param id 报警数据主键
     * @return 报警数据
     */
    @Override
    public AlertData selectAlertDataById(Long id)
    {
        return alertDataMapper.selectById(id);
    }

    /**
     * 查询报警数据列表
     * 
     * @param alertData 报警数据
     * @return 报警数据
     */
    @Override
    public List<AlertData> selectAlertDataList(AlertData alertData)
    {
        return alertDataMapper.selectAlertDataList(alertData);
    }

    /**
     * 新增报警数据
     * 
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int insertAlertData(AlertData alertData)
    {
        return alertDataMapper.insert(alertData);
    }

    /**
     * 修改报警数据
     * 
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int updateAlertData(AlertData alertData)
    {
        return alertDataMapper.updateById(alertData);
    }

    /**
     * 批量删除报警数据
     * 
     * @param ids 需要删除的报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataByIds(Long[] ids)
    {
        return alertDataMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警数据信息
     * 
     * @param id 报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataById(Long id)
    {
        return alertDataMapper.deleteById(id);
    }

    /**
     * 设备数据报警过滤
     */
    @Override
    public void deviceDataAlertFilter() {
        //1.统计规则数量,如果没有直接结束
        Long count = alertRuleMapper.selectCount(new LambdaQueryWrapper<AlertRule>().eq(AlertRule::getStatus, 1));
         if(count<=0){
             return;
         }
        //2.从Redis中获取所有上报设备数据
        List<List<DeviceData>> values = redisTemplate.opsForHash().values(Constants.IOT_DEVICE_LAST_DATA);
        //3.没有数据直接结束
        if(CollUtil.isEmpty(values)){
            return;
        }
        //4.开始遍历数据,处理每一条数据,是否满足报警规则
        List<DeviceData> allDeviceData = new ArrayList<>();
        values.forEach(allDeviceData::addAll);
        allDeviceData.forEach(this::dataAlertFilter);
    }

    /**
     * 设备数据报警过滤
     * @param data 设备数据
     */
    private void dataAlertFilter(DeviceData data) {
      //1.如果当前上报数据超过一分钟,忽略
        if(data.getAlarmTime().plusMinutes(1).isBefore(LocalDateTime.now())){
            return;
        }
        //2.获取产品下设备的对应物理模型规则
        List<AlertRule> alertRules = alertRuleMapper.selectList(
                new LambdaQueryWrapper<AlertRule>()
                        .eq(AlertRule::getProductKey, data.getProductKey())
                        .in(AlertRule::getIotId,-1, data.getIotId())
                        .eq(AlertRule::getFunctionId, data.getFunctionId())
                        .eq(AlertRule::getStatus, 1));
        //3.判断规则是否为空,为空直接结束
        if(CollUtil.isEmpty(alertRules)){
            return;
        }
        //4.遍历每一条规则
        alertRules.forEach(rule->dataIsmatchRule(rule,data));
    }


    /**
     * 数据是否符合报警规则
     * @param rule 报警规则
     * @param data 数据
     */
    private void dataIsmatchRule(AlertRule rule, DeviceData data) {
        String cycleKey=Constants.ALERT_TRIGGER_COUNT_PREFIX + data.getProductKey() + "_" + data.getIotId() + "_" + data.getFunctionId() + "_" + rule.getId();
        String silentKey = Constants.ALERT_SILENT_PREFIX + data.getProductKey() + "_" + data.getIotId() + "_" + data.getFunctionId() + "_" + rule.getId() ;
        String temp=Constants.TEMPORARY_KEY+data.getDeviceName()+"_"+data.getIotId();

        //1.判断是否在生效时间内
        if (!isEffectiveTime(rule,data)) {
            return;
        }
        //2.判断数据是否到达阈值
        if(!isMatchThreshold(rule,data)){
            redisTemplate.opsForHash().delete(Constants.ALARM_CYCLE_KEY,cycleKey);
            redisTemplate.delete(temp);
            return;
        }
        //3.判断沉默周期
        String silence = (String) redisTemplate.opsForValue().get(silentKey);
        if(StringUtils.isNotEmpty(silence)){
            return;
        }
        redisTemplate.opsForHash().put(temp,temp+"_"+LocalTime.now(),data);
        //4.查询并计算报警次数
        //1.获取报警次数
        Integer count = (Integer) redisTemplate.opsForHash().get(Constants.ALARM_CYCLE_KEY, cycleKey);
        if(ObjectUtil.isNull(count)){
            count = 1;
        }else {
            //2.报警次数+1
            count++;
        }
        if(count< rule.getDuration()){
            redisTemplate.opsForHash().put(Constants.ALARM_CYCLE_KEY,cycleKey,count);
            return;
        }
        //5删除报警次数,添加沉默周期
        redisTemplate.opsForHash().delete(Constants.ALARM_CYCLE_KEY,cycleKey);
        redisTemplate.opsForValue().set(silentKey,data.getDeviceName(),rule.getAlertSilentPeriod(), TimeUnit.MINUTES);
        //6.批量添加报警数据
        List<DeviceData> datas = (List<DeviceData>) redisTemplate.opsForHash().values(temp);
        addAllAlertData(rule,datas);
        redisTemplate.opsForHash().delete(Constants.TEMPORARY_KEY,temp);
    }

    /**
     * 判断数据是否在生效时间内
     * @param rule 报警规则
     * @param data 数据
     * @return 是否在生效时间内
     */
    private Boolean isEffectiveTime(AlertRule rule, DeviceData data) {
        String[] split = rule.getAlertEffectivePeriod().split("~");
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);
        LocalTime localTime = data.getAlarmTime().toLocalTime();
        if(localTime.isBefore(startTime) || localTime.isAfter(endTime)){
            return false;
        }
        return true;
    }

    /**
     * 判断数据是否到达阈值
     * @param rule 报警规则
     * @param data 数据
     * @return 是否到达阈值
     */
    private boolean isMatchThreshold(AlertRule rule, DeviceData data) {
        Double dataValue = Double.valueOf(data.getDataValue().toString());
        Double ruleValue = Double.valueOf(rule.getValue());
        return ">=".equals(rule.getOperator()) && Double.compare(ruleValue, dataValue) < 0
                || "<".equals(rule.getOperator()) && Double.compare(ruleValue, dataValue) >= 0;
    }



    /**
     * 批量添加报警数据
     * @param rule 报警规则
     */
    private void addAllAlertData(AlertRule rule,List<DeviceData> datas) {
        //查询报警数据
        List<Long> otherUserIds = sysUserRoleMapper.selectUserIdByRoleName("超级管理员");
           DeviceData data = datas.get(datas.size()-1);
            if(rule.getAlertDataType() == 0){
                if(data.getLocationType()==0){
                    otherUserIds.addAll(deviceMapper.selectNursingIdsByIotIdWithElder(data.getIotId()));
                }else {
                    otherUserIds.addAll(deviceMapper.selectNursingIdsByIotIdWithBed(data.getIotId()));
                }
            }else {
                otherUserIds.addAll(deviceMapper.selectNursingIdsByIotIdWithBed(data.getIotId()));
            }
       List<AlertData> alertDataList = new ArrayList<>();
       datas.forEach(temp->{
           otherUserIds.forEach(userId->{
               AlertData alertData = BeanUtil.toBean(temp, AlertData.class);
               alertData.setId(null);
               alertData.setAlertRuleId(rule.getId());
               String reason = StringUtils.format("报警原因:{}{}{}, 持续{}个周期", rule.getFunctionName(), rule.getOperator(), rule.getValue(), rule.getDuration());
               alertData.setAlertReason(reason);
               alertData.setType(rule.getAlertDataType());
               alertData.setStatus(Constants.ALERT_DATA_STATUS);
               alertData.setUserId(userId);
               alertDataList.add(alertData);
           });
       });
       this.saveBatch(alertDataList);

       //报警通知
       webSocketNotify(alertDataList.get(alertDataList.size()-1),rule,otherUserIds);
    }

    /**
     * 报警通知
     * @param alertData 报警数据
     * @param rule 报警规则
     * @param otherUserIds 通知用户ID
     */
    private void webSocketNotify(AlertData alertData, AlertRule rule, List<Long> otherUserIds) {
        //报警通知
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertData, AlertNotifyVo.class);
        alertNotifyVo.setFunctionName(rule.getFunctionName());
        alertNotifyVo.setAlertDataType(rule.getAlertDataType());
        alertNotifyVo.setVoiceNotifyStatus(0);
        alertNotifyVo.setNotifyType(1);
         if(alertNotifyVo.getLocationType()==1 && alertNotifyVo.getPhysicalLocationType()==2){
             alertNotifyVo.setIsAllConsumer(true);
         }else{
             alertNotifyVo.setIsAllConsumer(false);
         }
        webSocketServer.sendMessageToConsumer(alertNotifyVo,otherUserIds);
    }
}
